package com.testitems.config.security;

import com.testitems.enums.UrlPermitAllEnum;
import com.testitems.config.PasswordEncoderConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author GFH
 * @since 2021-11-20
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法注解方式来进行权限控制
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private PasswordEncoderConfig passwordEncoder;
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        /**
         * 自定义认证
         * 登录   com.meshop.crm.service.login#/login
         *      认证授权通过后返回jwtToken
         *          每次请求携带jwtToken即可
         *      认证失败返回对应的错误信息
         *              原型图
         *                  需提示: 账户不存在 、邮箱密码不匹配，请检查输入
         *  不需要认证的url  com.meshop.crm.enums.UrlPermitAllEnum
         *
         * 认证
         *      com.meshop.crm.security.JwtAuthenticationTokenFilter
         *      校验header头中带有jwtToken的 token信息
         * 认证失败 :com.meshop.crm.security.EntryPointUnauthorizedHandler
         */
        http
                .authorizeRequests()
                .antMatchers(UrlPermitAllEnum.getUrlList().toArray(new String[UrlPermitAllEnum.getUrlList().size()]) //不需要认证的url
                )
                .permitAll()
                .anyRequest()
                .authenticated()
                .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //
        ;
        // 允许跨域访问
        http.cors().disable();
        //开启模拟请求，比如API POST测试工具的测试，不开启时，API POST为报403错误
        http.csrf().disable();
        //自定token校验
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 处理异常情况：认证失败
        http.exceptionHandling().authenticationEntryPoint(entryPointUnauthorizedHandler);
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        //对于在 header 里面增加 token 等类似情况，放行所有 OPTIONS 请求。
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
        web.ignoring()
                .antMatchers("/webjars/**")
                .antMatchers("/v2/**")
                .antMatchers("/v2/api-docs-ext/**")
                .antMatchers("/swagger-resources/**")
                .antMatchers("/doc.html");
    }

    @Bean
    public AuthenticationProvider authenticationProvider(MyUserDetailService userDetailService) {
        final DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        //对默认的 UserDetailsService 进行覆盖
        authenticationProvider.setUserDetailsService(userDetailService);
        authenticationProvider.setPasswordEncoder(passwordEncoder.passwordEncoder());
        return authenticationProvider;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}
